#!/usr/bin/python

import logging
import random
import time
import threading
from song_db import SongDB
from video_player import VideoPlayer

class Karaoke(threading.Thread):
    def __init__(self, config):
        threading.Thread.__init__(self)

        self._config = config
        self._song_db = SongDB(config)

        # Variables below are protected by _lock
        self._lock = threading.RLock()
        self._audio_channel = VideoPlayer.AUDIO_LEFT
        self._current_playing = None
        self._is_terminated = False
        self._player = VideoPlayer()

        # Play List and its condition variable
        self._playlist_lock = threading.Condition()
        self._playlist = list()

        # Start the thread
        self.start()

    def run(self):
        while not self._IsTerminated():
            if not self._player.IsPlaying():
                self._lock.acquire()
                self._current_playing = None
                self._lock.release()
                self.Next(blocking=True)
            time.sleep(1)  # sleep for awhile and check again

    def AddToPlayList(self, singer_type, singer, song):
        """Adds the file to the play list"""
        filename = self._song_db.GetFilename(singer_type, singer, song)
        logging.debug('add file %s', filename)

        self._playlist_lock.acquire()
        self._playlist.append([singer, song, filename])
        self._playlist_lock.notify()
        self._playlist_lock.release()

    def _GetNext(self, blocking):
        """Pops and returns the next song in the play list"""
        self._playlist_lock.acquire()
        while not self._playlist and not self._IsTerminated() and blocking:
            self._playlist_lock.wait()
        if self._playlist:
            (singer, song, filename) = self._playlist.pop(0)
        else:
            (singer, song, filename) = (None, None, None)
        self._playlist_lock.release()
        return (singer, song, filename)

    def GetPlayList(self):
        """Returns a list of files in the play list"""
        value = []
        self._lock.acquire()
        if self._current_playing:
            value.append(self._current_playing)
        self._lock.release()

        self._playlist_lock.acquire()
        value.extend(self._playlist)
        self._playlist_lock.release()

        return value

    def GetSingers(self, singer_type):
        return self._song_db.GetSingers(singer_type)

    def GetSongs(self, singer_type, singer):
        return self._song_db.GetSongs(singer_type, singer)

    def _IsPlaying(self):
        self._lock.acquire()
        value = self._player.IsPlaying()
        self._lock.release()
        return value

    def _IsTerminated(self):
        self._lock.acquire()
        value = self._is_terminated
        self._lock.release()
        return value

    def Next(self, blocking=False):
        """Plays the next song in the play list"""
        (singer, song, filename) = self._GetNext(blocking)
        self._lock.acquire()
        if song:
            self._current_playing = (singer, song, filename)
            self._player.Play(filename, self._audio_channel)
        else:
            self._current_playing = None
            self._player.Stop()
        self._lock.release()

    def Terminate(self):
        """Stops and terminates the player"""
        self._lock.acquire()
        self._is_terminated = True
        self._player.Terminate()   # Kill the player as well
        self._lock.release()

        self._playlist_lock.acquire()
        self._playlist_lock.notify()
        self._playlist_lock.release()

        self.join()  # Wait until the thread is killed

    def ToggleAudioChannel(self):
        """Toggles the audio channel. Returns true if the new channel is KTV"""
        self._lock.acquire()
        if self._audio_channel == VideoPlayer.AUDIO_LEFT:
            self._audio_channel = VideoPlayer.AUDIO_RIGHT
            value = False
        else:
            self._audio_channel = VideoPlayer.AUDIO_LEFT
            value = True
        self._player.SetAudioChannel(self._audio_channel)
        self._lock.release()
        return value

    def Promote(self, number):
        """Move the specified song in the playlist to the top"""
        self._playlist_lock.acquire()
        if number >= 0 and number < len(self._playlist):
            l = self._playlist
            self._playlist = [l[number]] + l[0:number] + l[number+1:]
        self._playlist_lock.release()

    def Delete(self, number):
        """Delete the specified song in the playlist"""
        self._playlist_lock.acquire()
        if number >= 0 and number < len(self._playlist):
            l = self._playlist
            self._playlist = l[0:number] + l[number+1:]
        self._playlist_lock.notify()
        self._playlist_lock.release()

    def Shuffle(self):
        self._playlist_lock.acquire()
        random.shuffle(self._playlist)
        self._playlist_lock.release()

    def RandomAdd(self):
        """Add random songs to the playlist"""
        self._playlist_lock.acquire()

        for i in xrange(10):
            (singer, song, filename) = self._song_db.GetRandom()
            logging.debug('add file %s', filename)
            self._playlist.append([singer, song, filename])

        self._playlist_lock.notify()
        self._playlist_lock.release()
